# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Lv Shenkai
# @Time    : 2022/2/10 19:28
# @File    : recommend.py

from scipy.stats import rankdata

"""
    热门推荐
    推荐交互最多的物品
"""


class User_history:
    """
        单个user的历史
    """

    def __init__(self, user_id):
        """

        :param user_id:
        """
        self.user_id = user_id
        # 交互过的物品
        self.items = []
        # 交互过的item数,不包括重复的
        self.items_num = 0
        # 交互过的物品对应次数
        self.items_population = []

    def add_item(self, cur_item):
        """
            添加交互物品
        :param cur_item:
        :return:
        """
        # 以前购买过的物品，则增加交互次数
        if item in self.items:
            cur_item_index = self.items.index(cur_item)
            self.items_population[cur_item_index] += 1
        # 新物品，加入交互列表
        else:
            self.items.append(cur_item)
            self.items_population.append(1)
            self.items_num += 1

    def sort_items_by_population(self):
        """
            返回user的历史交互items根据交互次数排序后的items列表
        :return:
        """
        # 历史交互物品不超过1个，则不需要排序，直接返回
        if self.items_num <= 1:
            return self.items

        # 根据交互次数排序后的item编号
        items_sorted = [0 for _ in self.items]
        # 交互次数排名
        population_ranks = rankdata(self.items_population, method='ordinal')

        for i in range(len(self.items)):
            cur_idx = population_ranks[i] - 1
            items_sorted[cur_idx] = self.items[i]

        return items_sorted


def get_item_population_rank(cur_item, cur_user_history_list):
    """
        获得cur_item在推荐物品中的排名，user个人热门物品排在全局热门物品前面
    :param cur_item: 当前物品
    :param cur_user_history_list: 当前user的历史交互item列表
    :return:
    """
    # 不考虑单个user历史，当前物品在全局的流行度排名
    cur_item_population_rank = ranks[cur_item]
    # cur_user的历史交互中，在全局热门物品中的数目
    num_in_history = 0
    # 查找排在cur_item前面的items中，有多少个在user历史交互中
    for i in range(1, cur_item_population_rank):
        item_i_id = ranks.index(i)
        if item_i_id in cur_user_history_list:
            num_in_history += 1
    return cur_item_population_rank - num_in_history


item_num = 107
user_num = 584399

# 设置的最大测试事件数，0表示不设上限
test_events_num = 3000

# 训练、测试数据文件地址
path_train = r'../data_MF/train.txt'
path_test = r'../data_MF/test.txt'

train = open(path_train)
test = open(path_test)

# 全局物品流行度
items_population = [0 for _ in range(107)]

# user历史记录
users_dic = dict()

HR_k = [2, 3, 4, 5, 10, 20]

HR = [0 for _ in HR_k]

mar = 0.0

# train
for line in train.readlines():
    user, item = line.split()[:2]
    user = int(user)
    item = int(item)
    # 更新全局流行度
    items_population[item] += 1
    # 更新用户个人流行度
    if user not in users_dic:
        users_dic[user] = User_history(user)

    users_dic[user].add_item(item)

# 全局item流行度排序
ranks = rankdata(items_population, method='ordinal').tolist()

# 已经测试过的事件数
cur_test_events_num = 0

# test
for line in test.readlines():
    # 测试的事件数超过设置最大次数，就结束测试
    if 0 < test_events_num < cur_test_events_num:
        break

    user, item = line.split()[:2]
    user = int(user)
    item = int(item)

    cur_test_events_num += 1

    # 取出当前用户的历史记录
    if user in users_dic:
        user_history = users_dic[user]
    else:
        user_history = User_history(user)
    # 当前用户的历史交互item排序
    user_history_items = user_history.sort_items_by_population()

    # 当前物品在个人历史交互中，则从个人历史中进行推荐
    if item in user_history_items:
        cur_rank = user_history_items.index(item)
    # 否则，从全局流行物品中进行推荐
    else:
        # 历史交互过得物品数量
        cur_rank = get_item_population_rank(item, user_history_items)
    mar += cur_rank
    for i in range(len(HR)):
        if cur_rank <= HR_k[i]:
            HR[i] += 1

mar = round(mar / cur_test_events_num, 2)
for i_HR in range(len(HR)):
    HR[i_HR] = round(HR[i_HR] * 100 / cur_test_events_num, 2)
print('MAR:', mar)
print(HR_k, ':', HR)
